package com.common.util.http;

import com.alibaba.fastjson.JSON;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 提供通过HTTP协议获取内容的方法 <br/>
 * 所有提供方法中的params参数在内部不会进行自动的url encode，如果提交参数需要进行url encode，请调用方自行处理
 *
 * @author lushuifa
 * @Description: HTTP请求代理工具
 * @date 2016年11月18日 上午11:21:05
 */
public class HttpUtil {
    /**
     * logger
     */
//    private static Logger logger = Logger.getLogger(HttpUtil.class);

    /**
     * 支持的Http method
     */
    private static enum HttpMethod {
        POST, DELETE, GET, PUT, HEAD;
    }

    ;

    @SuppressWarnings({"unchecked", "rawtypes"})
    private static String invokeUrl(String url, Map params, Map<String, String> headers, int connectTimeout, int readTimeout, HttpMethod method) {
        //构造请求参数字符串
        StringBuilder paramsStr = null;
        if (params != null) {
            paramsStr = new StringBuilder();
            Set<Map.Entry> entries = params.entrySet();
            for (Map.Entry entry : entries) {
                String value = (entry.getValue() != null) ? (String.valueOf(entry.getValue())) : "";
                paramsStr.append(entry.getKey() + "=" + value + "&");
            }
            //只有POST方法才能通过OutputStream(即form的形式)提交参数
            if (method != HttpMethod.POST) {
                url += "?" + paramsStr.toString();
            }
        }
        URL uUrl = null;
        HttpURLConnection conn = null;
        BufferedWriter out = null;
        BufferedReader in = null;
        try {
            //创建和初始化连接
            uUrl = new URL(url);
            conn = (HttpURLConnection) uUrl.openConnection();
            if("post".equals(method.toString().toLowerCase().trim())){
                conn.setRequestProperty("content-type", "application/x-www-form-urlencoded");
            }
            conn.setRequestMethod(method.toString().toUpperCase());
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setConnectTimeout(connectTimeout);//设置连接超时时间
            conn.setReadTimeout(readTimeout);//设置读取超时时间
            //指定请求header参数
            if (headers != null && headers.size() > 0) {
                Set<String> headerSet = headers.keySet();
                for (String key : headerSet) {
                    conn.setRequestProperty(key, headers.get(key));
                }
            }
            if (paramsStr != null && method == HttpMethod.POST) {
                //发送请求参数
                out = new BufferedWriter(new OutputStreamWriter(conn.getOutputStream(), StandardCharsets.UTF_8.name()));
                out.write(paramsStr.toString());
                out.flush();
            }

            //接收返回结果
            StringBuilder result = new StringBuilder();
            in = new BufferedReader(new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8.name()));
            if (in != null) {
                String line = "";
                while ((line = in.readLine()) != null) {
                    result.append(line);
                }
            }
            return result.toString();
        } catch (Exception e) {
            e.printStackTrace();
//            logger.error("调用接口["+url+"]失败！请求URL："+url+"，参数："+params,e);
            //处理错误流，提高http连接被重用的几率
            try {
                byte[] buf = new byte[100];
                InputStream es = conn.getErrorStream();
                if (es != null) {
                    while (es.read(buf) > 0) {
                        ;
                    }
                    es.close();
                }
            } catch (Exception e1) {
                e1.printStackTrace();
            }
        } finally {
            try {
                if (out != null) {
                    out.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            //关闭连接
            if (conn != null) {
                conn.disconnect();
            }
        }
        return null;
    }

    public static String putByJson(String url, Map<String,Object> params){
        String responseInfo = "";
        //创建httpclient对象
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPut httpput = new HttpPut(url);
        httpput.setHeader("Cache-Control","no-cache");
        httpput.setHeader("Connection", "keep-alive");
        httpput.setHeader("Content-Type", "application/json;charset=UTF-8");
        //组织请求参数
        StringEntity stringEntity = new StringEntity(JSON.toJSONString(params),StandardCharsets.UTF_8.name());
        httpput.setEntity(stringEntity);
        CloseableHttpResponse  httpResponse = null;
        try {
            //响应信息
            httpResponse = client.execute(httpput);
            HttpEntity entity = httpResponse.getEntity();
            responseInfo = EntityUtils.toString(entity, StandardCharsets.UTF_8.name());
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                httpResponse.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        try {
            client.close();  //关闭连接、释放资源
        } catch (IOException e) {
            e.printStackTrace();
        }
        return responseInfo;
    }

    public static String deleteByJson(String url){
        return deleteByJson(url,null);
    }
    public static String deleteByJson(String url,Map<String,Object> params){
        String responseInfo = "";
        //创建httpclient对象
        CloseableHttpClient client = HttpClients.createDefault();
        HttpDeleteWithBody httpDeleteWithBody = new HttpDeleteWithBody(url);

        //设置header信息
        //        httpPost.setHeader("Content-type", "application/x-www-form-urlencoded");
        httpDeleteWithBody.setHeader("Cache-Control","no-cache");
        httpDeleteWithBody.setHeader("Connection", "keep-alive");
        httpDeleteWithBody.setHeader("Content-Type", "application/json;charset=UTF-8");

        //写入JSON数据参数
        if(params!=null) {
            StringEntity stringEntity = new StringEntity(JSON.toJSONString(params), StandardCharsets.UTF_8.name());
            httpDeleteWithBody.setEntity(stringEntity);
        }
        CloseableHttpResponse httpResponse = null;
        try {
            httpResponse = client.execute(httpDeleteWithBody);
            //获取结果实体
            HttpEntity entity = httpResponse.getEntity();
            if (entity != null) {
                //按指定编码转换结果实体为String类型
                responseInfo = EntityUtils.toString(entity, StandardCharsets.UTF_8.name());
            }
            EntityUtils.consume(entity);
            //释放链接
            httpResponse.close();
            client.close();
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            try {
                httpResponse.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseInfo;
    }

    public static Map postByJson(String url, Map<String,Object> params){
        return postByJson(url, JSON.toJSONString(params));
    }
    public static Map postByJson(String url, String paramJSON){
        Map<String,Object> result = new HashMap<>();
        String responseInfo = "";
        //创建httpclient对象
        CloseableHttpClient client = HttpClients.createDefault();
        //创建post方式请求对象
        HttpPost httpPost = new HttpPost(url);
        //装填参数
        ContentType contentType = ContentType.create("application/json", StandardCharsets.UTF_8.name());
        httpPost.setEntity(new StringEntity(paramJSON, contentType));

        //设置header信息
        //        httpPost.setHeader("Content-type", "application/x-www-form-urlencoded");
        httpPost.setHeader("Content-type", "application/json;charset=UTF-8");
        httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");

        //执行请求操作，并拿到结果（同步阻塞）
        CloseableHttpResponse httpResponse = null;
        try {
            httpResponse = client.execute(httpPost);
            result.put("status", httpResponse.getStatusLine().getStatusCode());
            //获取结果实体
            HttpEntity entity = httpResponse.getEntity();
            if (entity != null) {
                //按指定编码转换结果实体为String类型
                result.put("data", JSON.parseObject(EntityUtils.toString(entity, StandardCharsets.UTF_8.name())));
            }
            //一定要记得把entity fully consume掉，否则连接池中的connection就会一直处于占用状态
            EntityUtils.consume(entity);
            //释放链接
            httpResponse.close();
            client.close();
        }catch (Exception e){
            e.printStackTrace();
        }finally{
            try {
                client.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

    /**
     * POST表单方法提交Http请求，语义为“增加” <br/>
     * 注意：Http方法中只有POST方法才能使用body来提交内容
     *
     * @param url            资源路径（如果url中已经包含参数，则params应该为null）
     * @param params         参数
     * @param connectTimeout 连接超时时间（单位为ms）
     * @param readTimeout    读取超时时间（单位为ms）
     * @return
     */
    public static String post(String url, Map<String, Object> params, int connectTimeout, int readTimeout) {
        return invokeUrl(url, params, null, connectTimeout, readTimeout, HttpMethod.POST);
    }

    /**
     * POST方法提交Http请求，语义为“增加” <br/>
     * 注意：Http方法中只有POST方法才能使用body来提交内容
     *
     * @param url            资源路径（如果url中已经包含参数，则params应该为null）
     * @param params         参数
     * @param headers        请求头参数
     * @param connectTimeout 连接超时时间（单位为ms）
     * @param readTimeout    读取超时时间（单位为ms）
     * @return
     */
    public static String post(String url, Map<String, Object> params, Map<String, String> headers, int connectTimeout, int readTimeout) {
        return invokeUrl(url, params, headers, connectTimeout, readTimeout, HttpMethod.POST);
    }

    /**
     * GET方法提交Http请求，语义为“查询”
     *
     * @param url 资源路径（如果url中已经包含参数，则params应该为null）
     * @return
     */
    public static String get(String url) {
        return invokeUrl(url, null, null, 100000, 100000, HttpMethod.GET);
    }

    /**
     * GET方法提交Http请求，语义为“查询”
     *
     * @param url            资源路径（如果url中已经包含参数，则params应该为null）
     * @param params         参数
     * @param connectTimeout 连接超时时间（单位为ms）
     * @param readTimeout    读取超时时间（单位为ms）
     * @return
     */
    public static String get(String url, Map<String, Object> params, int connectTimeout, int readTimeout) {
        return invokeUrl(url, params, null, connectTimeout, readTimeout, HttpMethod.GET);
    }

    /**
     * GET方法提交Http请求，语义为“查询”
     *
     * @param url            资源路径（如果url中已经包含参数，则params应该为null）
     * @param params         参数
     * @param headers        请求头参数
     * @param connectTimeout 连接超时时间（单位为ms）
     * @param readTimeout    读取超时时间（单位为ms）
     * @return
     */
    public static String get(String url, Map<String, Object> params, Map<String, String> headers, int connectTimeout, int readTimeout) {
        return invokeUrl(url, params, headers, connectTimeout, readTimeout, HttpMethod.GET);
    }

    /**
     * PUT方法提交Http请求，语义为“更改” <br/>
     * 注意：PUT方法也是使用url提交参数内容而非body，所以参数最大长度收到服务器端实现的限制，Resin大概是8K
     *
     * @param url    资源路径（如果url中已经包含参数，则params应该为null）
     * @return
     */
    public static String put(String url) {
        return invokeUrl(url, null, null, 5000, 5000, HttpMethod.PUT);
    }

    /**
     * PUT方法提交Http请求，语义为“更改” <br/>
     * 注意：PUT方法也是使用url提交参数内容而非body，所以参数最大长度收到服务器端实现的限制，Resin大概是8K
     *
     * @param url    资源路径（如果url中已经包含参数，则params应该为null）
     * @param params 参数
     * @return
     */
    public static String put(String url, Map<String, Object> params) {
        return invokeUrl(url, params, null, 5000, 5000, HttpMethod.PUT);
    }

    /**
     * PUT方法提交Http请求，语义为“更改” <br/>
     * 注意：PUT方法也是使用url提交参数内容而非body，所以参数最大长度收到服务器端实现的限制，Resin大概是8K
     *
     * @param url            资源路径（如果url中已经包含参数，则params应该为null）
     * @param params         参数
     * @param connectTimeout 连接超时时间（单位为ms）
     * @param readTimeout    读取超时时间（单位为ms）
     * @return
     */
    public static String put(String url, Map<String, Object> params, int connectTimeout, int readTimeout) {
        return invokeUrl(url, params, null, connectTimeout, readTimeout, HttpMethod.PUT);
    }

    /**
     * PUT方法提交Http请求，语义为“更改” <br/>
     * 注意：PUT方法也是使用url提交参数内容而非body，所以参数最大长度收到服务器端实现的限制，Resin大概是8K
     *
     * @param url            资源路径（如果url中已经包含参数，则params应该为null）
     * @param params         参数
     * @param headers        请求头参数
     * @param connectTimeout 连接超时时间（单位为ms）
     * @param readTimeout    读取超时时间（单位为ms）
     * @return
     */
    public static String put(String url, Map<String, Object> params, Map<String, String> headers, int connectTimeout, int readTimeout) {
        return invokeUrl(url, params, headers, connectTimeout, readTimeout, HttpMethod.PUT);
    }

    /**
     * DELETE方法提交Http请求，语义为“删除”
     *
     * @param url            资源路径（如果url中已经包含参数，则params应该为null）
     * @param params         参数
     * @param connectTimeout 连接超时时间（单位为ms）
     * @param readTimeout    读取超时时间（单位为ms）
     * @return
     */
    public static String delete(String url, Map<String, Object> params, int connectTimeout, int readTimeout) {
        return invokeUrl(url, params, null, connectTimeout, readTimeout, HttpMethod.DELETE);
    }

    /**
     * DELETE方法提交Http请求，语义为“删除”
     *
     * @param url            资源路径（如果url中已经包含参数，则params应该为null）
     * @param params         参数
     * @param headers        请求头参数
     * @param connectTimeout 连接超时时间（单位为ms）
     * @param readTimeout    读取超时时间（单位为ms）
     * @return
     */
    public static String delete(String url, Map<String, Object> params, Map<String, String> headers, int connectTimeout, int readTimeout) {
        return invokeUrl(url, params, headers, connectTimeout, readTimeout, HttpMethod.DELETE);
    }

    /**
     * HEAD方法提交Http请求，语义同GET方法  <br/>
     * 跟GET方法不同的是，用该方法请求，服务端不返回message body只返回头信息，能节省带宽
     *
     * @param url            资源路径（如果url中已经包含参数，则params应该为null）
     * @param params         参数
     * @param connectTimeout 连接超时时间（单位为ms）
     * @param readTimeout    读取超时时间（单位为ms）
     * @return
     */
    public static String head(String url, Map<String, Object> params, int connectTimeout, int readTimeout) {
        return invokeUrl(url, params, null, connectTimeout, readTimeout, HttpMethod.HEAD);
    }

    /**
     * HEAD方法提交Http请求，语义同GET方法  <br/>
     * 跟GET方法不同的是，用该方法请求，服务端不返回message body只返回头信息，能节省带宽
     *
     * @param url            资源路径（如果url中已经包含参数，则params应该为null）
     * @param params         参数
     * @param headers        请求头参数
     * @param connectTimeout 连接超时时间（单位为ms）
     * @param readTimeout    读取超时时间（单位为ms）
     * @return
     */
    public static String head(String url, Map params, Map<String, String> headers, int connectTimeout, int readTimeout) {
        return invokeUrl(url, params, headers, connectTimeout, readTimeout, HttpMethod.HEAD);
    }

    public static void main(String[] args) {
        //Map params = new HashMap();
        //params.put("phoneNo", "中文");
        //String str = HttpUtil.get("http://localhost:9092/elis_smp_als_dmz/do/app/activitySupport/demo", params, 3000, 3000);
        //System.out.println(str);
        System.out.println(StandardCharsets.UTF_8.name());
    }

}
